package com.java.modebing.test.aop;

import com.java.modebing.test.annotation.RepeatSubmit;
import com.java.modebing.test.config.RedisCache;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 定义一个切面类
 **/
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class RepeatSubmitAspect {

 private RedisCache redisCache;
    /**
     * 定义 @Pointcut注解表达式, 通过特定的规则来筛选连接点, 就是Pointcut，选中那几个你想要的方法
     * 在程序中主要体现为书写切入点表达式（通过通配、正则表达式）过滤出特定的一组 JointPoint连接点
     * 方式一：@annotation：当执行的方法上拥有指定的注解时生效（本博客采用这）
     * 方式二：execution：一般用于指定方法的执行
     */
    @Pointcut("@annotation(repeatSubmit)")
    public void pointCutNoRepeatSubmit(RepeatSubmit repeatSubmit) {

    }
 
    /**
     * 环绕通知, 围绕着方法执行
     * @param joinPoint
     * @param repeatSubmit
     * @return
     * @throws Throwable
     * @Around 可以用来在调用一个具体方法前和调用后来完成一些具体的任务。
     * <p>
     * 方式一：单用 @Around("execution(* net.wnn.controller.*.*(..))")可以
     * 方式二：用@Pointcut和@Around联合注解也可以（本博客采用这个）
     * <p>
     * <p>
     * 两种方式
     * 方式一：加锁 固定时间内不能重复提交
     * <p>
     * 方式二：先请求获取token，这边再删除token,删除成功则是第一次提交
     */
    @Around("pointCutNoRepeatSubmit(repeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        long start = System.currentTimeMillis();
        Object[] args = joinPoint.getArgs();
        log.info(Arrays.toString(args));
        List<Object> values = Lists.newArrayList();
        for (Object arg : args) {
            for (String column : repeatSubmit.columns()) {
                Field field = ReflectionUtils.findField(arg.getClass(), column);
                if (field != null){
                    ReflectionUtils.makeAccessible(field);
                    Object value = ReflectionUtils.getField(field,arg);
                    values.add(value);
                }
            }

        }
        Object proceed1 = null;
        if (!values.isEmpty()){
            String key = "vms:app:" + repeatSubmit.sense().getCode() + ":";
            for (Object name : values) {
                key += name.toString() + ":";
            }
            key = key.substring(0, key.length() - 1);
            log.info("==========================redis key:{}======================",key);
            while (!redisCache.getRedisTemplate().opsForValue().setIfAbsent(key, 1,10,TimeUnit.SECONDS)){
                Thread.sleep(100);
            }
            try {
                proceed1 = joinPoint.proceed();
            } finally {
                redisCache.deleteObject(key);
            }
        }else {
            proceed1 = joinPoint.proceed();
        }

        log.info("=========耗时：{}========",System.currentTimeMillis()-start);
        return proceed1;
    }
}